#include "mydragger.h"

#include <osg/Geometry>
#include <osg/Geode>
#include <osg/LineWidth>
#include <osg/PolygonMode>
#include <osg/ShapeDrawable>
#include <osg/Material>

#include <osgViewer/View>
#include <osgText/Text>

#include <osgUtil/SmoothingVisitor>
MoveDragger::MoveDragger(void)
{
	_xDragger = new osgManipulator::Translate1DDragger(osg::Vec3(0.0,0.0,0.0), 
		osg::Vec3(0.0,0.0,1.0));
	//_xDragger->setColor(osg::Vec4(0.0f, 0.0f, 0.7f, 1.0f));
	addChild(_xDragger.get());
	addDragger(_xDragger.get());

	_yDragger = new osgManipulator::Translate1DDragger(osg::Vec3(0.0,0.0,0.0), 
		osg::Vec3(0.0,0.0,1.0));
	//_yDragger->setColor(osg::Vec4(0.7f, 0.0f, 0.0f, 1.0f));
	addChild(_yDragger.get());
	addDragger(_yDragger.get());

	_zDragger = new osgManipulator::Translate1DDragger(osg::Vec3(0.0,0.0,0.0), 
		osg::Vec3(0.0,0.0,1.0));
	//_zDragger->setColor(osg::Vec4(0.0f, 0.7f, 0.0f, 1.0f));
	addChild(_zDragger.get());
	addDragger(_zDragger.get());

	_xoyDragger = new osgManipulator::Translate2DDragger();
	//_xoyDragger->setColor(osg::Vec4(0.7f, 0.7f, 0.7f, 1.0f));
	addChild(_xoyDragger.get());
	addDragger(_xoyDragger.get());

	_xozDragger = new osgManipulator::Translate2DDragger();
	//_xozDragger->setColor(osg::Vec4(0.7f, 0.7f, 0.7f, 1.0f));
	addChild(_xozDragger.get());
	addDragger(_xozDragger.get());

	_yozDragger = new osgManipulator::Translate2DDragger();
	//_yozDragger->setColor(osg::Vec4(0.7f, 0.7f, 0.7f, 1.0f));
	addChild(_yozDragger.get());
	addDragger(_yozDragger.get());

	setParentDragger(getParentDragger());
}

MoveDragger::~MoveDragger(void)
{

}

void MoveDragger::setupDefaultGeometry()
{
	/*
	// Create a line.
	osg::Geode* lineGeode = new osg::Geode;
	{
		osg::Geometry* geometry = new osg::Geometry();

		osg::Vec3Array* vertices = new osg::Vec3Array(2);
		(*vertices)[0] = osg::Vec3(0.0f,0.0f,0.2f);
		(*vertices)[1] = osg::Vec3(0.0f,0.0f,1.0f);

		geometry->setVertexArray(vertices);
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,2));

		lineGeode->addDrawable(geometry);
	}

	// Turn of lighting for line and set line width.
	{
		osg::LineWidth* linewidth = new osg::LineWidth();
		linewidth->setWidth(2.0f);
		lineGeode->getOrCreateStateSet()->setAttributeAndModes(linewidth, osg::StateAttribute::ON);
		lineGeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
	}

	// Add line to all the individual 1D draggers.
	_xDragger->addChild(lineGeode);
	_yDragger->addChild(lineGeode);
	_zDragger->addChild(lineGeode);

	osg::Geode* geode = new osg::Geode;

	// Create a cone.
	{
		osg::Cone* cone = new osg::Cone (osg::Vec3(0.0f, 0.0f, 1.0f), 0.03f, 0.15f);
		geode->addDrawable(new osg::ShapeDrawable(cone));
		geode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
	}

	// Create an invisible cylinder for picking the line.
	{
		osg::Cylinder* cylinder = new osg::Cylinder (osg::Vec3(0.0f,0.0f,0.5f), 0.04f, 1.0f);
		osg::Drawable* geometry = new osg::ShapeDrawable(cylinder);
		osgManipulator::setDrawableToAlwaysCull(*geometry);
		geode->addDrawable(geometry);
	}

	// Add geode to all 1D draggers.
	_xDragger->addChild(geode);
	_yDragger->addChild(geode);
	_zDragger->addChild(geode);

	// Rotate X-axis dragger appropriately.
	{
		osg::Quat rotation; 
		rotation.makeRotate(osg::Vec3(0.0f, 0.0f, 1.0f), osg::Vec3(1.0f, 0.0f, 0.0f));
		_xDragger->setMatrix(osg::Matrix(rotation));
	}

	// Rotate Y-axis dragger appropriately.
	{
		osg::Quat rotation; 
		rotation.makeRotate(osg::Vec3(0.0f, 0.0f, 1.0f), osg::Vec3(0.0f, 1.0f, 0.0f));
		_yDragger->setMatrix(osg::Matrix(rotation));
	}
	_xDragger->setColor(osg::Vec4(1.0f,0.0f,0.0f,1.0f));
	_yDragger->setColor(osg::Vec4(0.0f,1.0f,0.0f,1.0f));
	_zDragger->setColor(osg::Vec4(0.0f,0.0f,1.0f,1.0f));

*/
/*
	osg::Geode* lineGeode = new osg::Geode;
	{
		osg::Geometry* geometry = new osg::Geometry();

		osg::Vec3Array* vertices = new osg::Vec3Array(2);
		(*vertices)[0] = osg::Vec3(0.0f,0.0f,0.2f);
		(*vertices)[1] = osg::Vec3(0.0f,0.0f,1.0f);

		geometry->setVertexArray(vertices);
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,2));

		lineGeode->addDrawable(geometry);
	}

	// Turn of lighting for line and set line width.
	{
		osg::LineWidth* linewidth = new osg::LineWidth();
		linewidth->setWidth(1.5f);
		lineGeode->getOrCreateStateSet()->setAttributeAndModes(linewidth, osg::StateAttribute::ON);
		lineGeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
	}

	// Add line to all the individual 1D draggers.
	_xDragger->addChild(lineGeode);
	_yDragger->addChild(lineGeode);
	_zDragger->addChild(lineGeode);

	osg::Geode* geode = new osg::Geode;

	// Create a cone.
	{
		osg::Cone* cone = new osg::Cone (osg::Vec3(0.0f, 0.0f, 1.0f), 0.03f, 0.15f);
		geode->addDrawable(new osg::ShapeDrawable(cone));
		geode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
	}

	// Create an invisible cylinder for picking the line.
	{
		osg::Cylinder* cylinder = new osg::Cylinder (osg::Vec3(0.0f,0.0f,0.5f), 0.04f, 1.0f);
		osg::Drawable* geometry = new osg::ShapeDrawable(cylinder);
		osgManipulator::setDrawableToAlwaysCull(*geometry);
		geode->addDrawable(geometry);
	}

	// Add geode to all 1D draggers.
	_xDragger->addChild(geode);
	_yDragger->addChild(geode);
	_zDragger->addChild(geode);
*/


	osg::Vec4 xColor = osg::Vec4(1.0f,0.0f,0.0f,1.0f);
	osg::Vec4 yColor = osg::Vec4(0.0f,1.0f,0.0f,1.0f);
	osg::Vec4 zColor = osg::Vec4(0.0f,0.0f,1.0f,1.0f);
	_xDragger->addChild(createAsix("x", xColor));
	_yDragger->addChild(createAsix("y", yColor));
	_zDragger->addChild(createAsix("z", zColor));
	_xDragger->setColor(xColor);
	_yDragger->setColor(yColor);
	_zDragger->setColor(zColor);


	// Rotate X-axis dragger appropriately.
	{
		osg::Quat rotation; 
		rotation.makeRotate(osg::Vec3(0.0f, 0.0f, 1.0f), osg::Vec3(1.0f, 0.0f, 0.0f));
		_xDragger->setMatrix(osg::Matrix(rotation));
	}

	// Rotate Y-axis dragger appropriately.
	{
		osg::Quat rotation; 
		rotation.makeRotate(osg::Vec3(0.0f, 0.0f, 1.0f), osg::Vec3(0.0f, 1.0f, 0.0f));
		_yDragger->setMatrix(osg::Matrix(rotation));
	}


	//xoz->xoy
	{
		osg::Quat rotation; 
		rotation.makeRotate(osg::Vec3(0.0f, 0.0f, 1.0f), osg::Vec3(0.0f, 1.0f, 0.0f));
		_xoyDragger->setMatrix(osg::Matrix(rotation));
	}
	//xoz->yoz
	{
		osg::Quat rotation; 
		rotation.makeRotate(osg::Vec3(1.0f, 0.0f, 0.0f), osg::Vec3(0.0f, 1.0f, 0.0f));
		_yozDragger->setMatrix(osg::Matrix(rotation));
	}

	{
	/*	osg::Geode* geode = new osg::Geode;
		osg::Geometry* geometry = new osg::Geometry();

		osg::Vec3Array* vertices = new osg::Vec3Array(4);
		(*vertices)[0] = osg::Vec3(0.0,0.0,0.5);
		(*vertices)[1] = osg::Vec3(0.0,0.0,0.0);
		(*vertices)[2] = osg::Vec3(0.5,0.0,0.0);
		(*vertices)[3] = osg::Vec3(0.5,0.0,0.5);

		geometry->setVertexArray(vertices);
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,vertices->size()));

		osg::Vec3Array* normals = new osg::Vec3Array;
		normals->push_back(osg::Vec3(0.0,1.0,0.0));
		geometry->setNormalArray(normals);
		geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);

		geode->addDrawable(geometry);

		osg::PolygonMode* polymode = new osg::PolygonMode;
		polymode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE);
		geode->getOrCreateStateSet()->setAttributeAndModes(polymode,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);

		geode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
*/
		_xoyDragger->addChild(createPlane(yColor,xColor));
		_yozDragger->addChild(createPlane(zColor,yColor));
		_xozDragger->addChild(createPlane(zColor,xColor));
	}

}

bool MoveDragger::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
	if (ea.getHandled()) return false;

	osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
	if (!view) return false;

	bool handled = false;

	bool activationPermitted = true;
	if (_activationModKeyMask!=0 || _activationKeyEvent!=0)
	{
		_activationPermittedByModKeyMask = (_activationModKeyMask!=0) ?
			((ea.getModKeyMask() & _activationModKeyMask)!=0) :
		false;

		if (_activationKeyEvent!=0)
		{
			switch (ea.getEventType())
			{
			case osgGA::GUIEventAdapter::KEYDOWN:
				{
					if (ea.getKey()==_activationKeyEvent) _activationPermittedByKeyEvent = true;
					break;
				}
			case osgGA::GUIEventAdapter::KEYUP:
				{
					if (ea.getKey()==_activationKeyEvent) _activationPermittedByKeyEvent = false;
					break;
				}
			default:
				break;
			}
		}

		activationPermitted =  _activationPermittedByModKeyMask || _activationPermittedByKeyEvent;

	}

	if (activationPermitted || _draggerActive)
	{
		switch (ea.getEventType())
		{
		case osgGA::GUIEventAdapter::PUSH:
			{
				osgUtil::LineSegmentIntersector::Intersections intersections;

				_pointer.reset();

				if (view->computeIntersections(ea.getX(),ea.getY(),intersections, _intersectionMask))
				{
					for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin();
						hitr != intersections.end();
						++hitr)
					{
						_pointer.addIntersection(hitr->nodePath, hitr->getLocalIntersectPoint());
					}
					
					bool find = false;
					for (osgManipulator::PointerInfo::IntersectionList::iterator it = _pointer._hitList.begin(); it!= _pointer._hitList.end(); it++)
					{

						for (osg::NodePath::iterator itr = it->first.begin();
							itr != it->first.end();
							++itr)
						{
							osgManipulator::Dragger* dragger = dynamic_cast<osgManipulator::Dragger*>(*itr);
							if (dragger)
							{
								if (dragger==this)
								{
									osg::Camera *rootCamera = view->getCamera();
									osg::NodePath nodePath = _pointer._hitList.front().first;
									osg::NodePath::reverse_iterator ritr;
									for(ritr = nodePath.rbegin();
										ritr != nodePath.rend();
										++ritr)
									{
										osg::Camera* camera = dynamic_cast<osg::Camera*>(*ritr);
										if (camera && (camera->getReferenceFrame()!=osg::Transform::RELATIVE_RF || camera->getParents().empty()))
										{
											rootCamera = camera;
											break;
										}
									}

									_pointer.setCamera(rootCamera);
									_pointer.setMousePosition(ea.getX(), ea.getY());

									dragger->handle(_pointer, ea, aa);
									dragger->setDraggerActive(true);
									_itr = it;
									handled = true;
									find = true;
								}
							}
						}
						if(find)
							break;
					}
				}
			}
		case osgGA::GUIEventAdapter::DRAG:
		case osgGA::GUIEventAdapter::RELEASE:
			{
				if (_draggerActive)
				{
					//_pointer._hitIter = _pointer._hitList.begin();
					_pointer._hitIter = _itr;
					//                    _pointer.setCamera(view->getCamera());
					_pointer.setMousePosition(ea.getX(), ea.getY());

					handle(_pointer, ea, aa);

					handled = true;
				}
				break;
			}
		default:
			break;
		}

		if (_draggerActive && ea.getEventType() == osgGA::GUIEventAdapter::RELEASE)
		{
			setDraggerActive(false);
			_pointer.reset();
		}
	}

	return handled;
}

bool MoveDragger::handle(const osgManipulator::PointerInfo& pi, const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
	// Check if the dragger node is in the nodepath.
	if (!pi.contains(this))
		return false;

	for (DraggerList::iterator itr=_draggerList.begin(); itr!=_draggerList.end(); ++itr)
	{
		if ((*itr)->handle(pi, ea, aa))
			return true;
	}
	return false;
}

osg::Geode* MoveDragger::createAsix(std::string tex ,osg::Vec4 color)
{
	osg::ref_ptr<osg::Geode> rootGeode = new osg::Geode;

	rootGeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
	{
		osg::Geometry* geometry = new osg::Geometry();
		geometry->setUseDisplayList(false);
		osg::Vec3Array* vertices = new osg::Vec3Array(2);
		(*vertices)[0] = osg::Vec3(0.0f,0.0f,0.25f);
		(*vertices)[1] = osg::Vec3(0.0f,0.0f,1.0f);
		
		geometry->setVertexArray(vertices);
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,2));
//
		//osg::ref_ptr<osg::DrawElementsUByte> element = new osg::DrawElementsUByte;
		//element->setMode(osg::PrimitiveSet::LINES);
		//element->push_back(0);
		//element->push_back(1);
		//geometry->addPrimitiveSet(element.get());
//
		rootGeode->addDrawable(geometry);
	}

	{
		osg::LineWidth* linewidth = new osg::LineWidth();
		linewidth->setWidth(1.5f);
		rootGeode->getOrCreateStateSet()->setAttributeAndModes(linewidth, osg::StateAttribute::ON);

	}

	{
		osg::Cylinder* cylinder = new osg::Cylinder (osg::Vec3(0.0f,0.0f,0.5f), 0.04f, 1.0f);
		osg::Drawable* geometry = new osg::ShapeDrawable(cylinder);
		osgManipulator::setDrawableToAlwaysCull(*geometry);
		rootGeode->addDrawable(geometry);
	}

	{
		//osg::Cone* cone = new osg::Cone (osg::Vec3(0.0f, 0.0f, 1.0f), 0.03f, 0.15f);
		//osg::ShapeDrawable* shape = new osg::ShapeDrawable(cone);
		//shape->setUseDisplayList(false);
		//shape->setColor(color);
		//rootGeode->addDrawable(shape);

	}

	{
		osgText::Font *font = osgText::readFontFile("./font/arial.ttf");
		osgText::Text* text = new osgText::Text();
		text->setUseDisplayList(false);
		text->setText(tex);
		text->setFont(font);
		text->setColor(color);
		text->setCharacterSize(26.0);
		text->setFontResolution(32,32);
		text->setPosition(osg::Vec3(0.0f ,0.0f ,1.02f));
		text->setAutoRotateToScreen(true);
		text->setCharacterSizeMode(osgText::TextBase::SCREEN_COORDS);
		text->setAlignment(osgText::TextBase::CENTER_BOTTOM);
		rootGeode->addDrawable(text);
	}
	//osgUtil::SmoothingVisitor smooth;
	//rootGeode->accept(smooth);
	return rootGeode.release();
}

osg::Group* MoveDragger::createPlane(osg::Vec4 color1, osg::Vec4 color2)
{
	osg::Group* root = new osg::Group;
	root->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);

	{
		osg::Geode* planeGeode = new osg::Geode;
		osg::Geometry* geometry = new osg::Geometry();
		geometry->setUseDisplayList(false);
		geometry->setDataVariance(osg::Object::DYNAMIC);
		osg::Vec3Array* vertices = new osg::Vec3Array(4);
		(*vertices)[0] = osg::Vec3(0.05f,0.0f,0.35f);
		(*vertices)[1] = osg::Vec3(0.05f,0.0f,0.05f);
		(*vertices)[2] = osg::Vec3(0.35f,0.0f,0.05f);
		(*vertices)[3] = osg::Vec3(0.35f,0.0f,0.35f);

		geometry->setVertexArray(vertices);
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,vertices->size()));
		//osgManipulator::setDrawableToAlwaysCull(*geometry);
		osg::Vec3Array* normals = new osg::Vec3Array;
		normals->push_back(osg::Vec3(0.0,1.0,0.0));
		geometry->setNormalArray(normals);
		geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
		
		//osg::Vec4Array* color = new osg::Vec4Array(1);
		//color->push_back(osg::Vec4(0.5,0.5,0.5,1.0));
		//geometry->setColorArray(color);
		//geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
		osg::PolygonMode* polymode = new osg::PolygonMode;
		polymode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE);
		planeGeode->getOrCreateStateSet()->setAttributeAndModes(polymode,osg::StateAttribute::ON);

		//osg::ref_ptr<osg::Material> matirial = new osg::Material;
		//matirial->setColorMode(osg::Material::DIFFUSE);
		//matirial->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 0, 1));
		//matirial->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(1, 1, 1, 1));
		//matirial->setShininess(osg::Material::FRONT_AND_BACK, 64.0f);
		//planeGeode->getOrCreateStateSet()->setAttributeAndModes(matirial.get(), osg::StateAttribute::ON);

		planeGeode->addDrawable(geometry);
		root->addChild(planeGeode);
	}

	{
		osg::Geode* lineGeode = new osg::Geode;
		osg::Geometry* geometry = new osg::Geometry();
		geometry->setUseDisplayList(false);
		geometry->setDataVariance(osg::Object::DYNAMIC);
		osg::Vec3Array* vertices = new osg::Vec3Array(3);
		(*vertices)[0] = osg::Vec3(0.0f,0.0f,0.4f);
		(*vertices)[1] = osg::Vec3(0.4f,0.0f,0.4f);
		(*vertices)[2] = osg::Vec3(0.4f,0.0f,0.0f);
		geometry->setVertexArray(vertices);
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,2));
		geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,1,2));

		osg::Vec4Array* colors = new osg::Vec4Array;
		//colors->push_back(osg::Vec4(1.0,1.0,1.0,1.0));
		colors->push_back(color1);
		colors->push_back(color2);
		geometry->setColorArray(colors);
		geometry->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET);
		lineGeode->addDrawable(geometry);
		root->addChild(lineGeode);

	}


	//osg::PolygonMode* polymode = new osg::PolygonMode;
	//polymode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE);
	//geode->getOrCreateStateSet()->setAttributeAndModes(polymode,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);

	//rootGeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);

	return root;
}
